﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms;
using Microsoft.Win32;
using System.IO;
using System.Diagnostics;

namespace MDigman.Camera.Media.Copier.WPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        enum FileType
        {
            Image = 1,
            Video,
            None
        }

        private bool bOverwrite = true;
        private bool bShowVideo = false;
        private string imageFormat = "_8";
        private string videoFormat = "_4";

        #region Constants
        private const string PROGRAM = "Camera Media Copier";
        private const string REG_KEY = "SOFTWARE\\MDigman\\CameraMediaCopier";
        private const string REG_SRC = "Folder_Src";
        private const string REG_IMG = "Folder_Image";
        private const string REG_VID = "Folder_Video";
        private const string REG_IMAGE_FMT = "Format_Image";
        private const string REG_VIDEO_FMT = "Format_Video";
        private const string REG_SRC_HIST = "History_Src_{0}";
        private const string REG_IMG_HIST = "History_Image_{0}";
        private const string REG_VID_HIST = "History_Video_{0}";
        private const string REG_OVERWRITE = "Overwrite_File";
        private const string REG_SHOW_VID = "Show_Video_Image";
        private const string CANON_DIGITAL = "Canon DIGITAL ";
        private const string JPG = ".JPG";
        private const string AVI = ".AVI";
        private const string FORMAT_1 = "{0}\\{1}";
        private const string FORMAT_2 = "{0}...\\{1}";
        private const string FORMAT_3 = "{0} {1}";
        private const string FORMAT_4 = "{0}\n{1}";
        private const int MAX_LENGTH = 60;
        // The following could be added as resources.
        private const string DESC_SRC = "Please select the directory on your memory card where the pictures are stored.";
        private const string DESC_IMG = "Please select the directory where you want your pictures to be copied.";
        private const string DESC_VID = "Please select the directory where you want your videos to be copied.";
        private const string FILE_EXISTS = "File already exists";
        private const string OVERWRITE = "Do you wish to overwrite this file?";
        private const string ABOUT = "Written by Michael Digman - Version";
        private const string OK = "OK";
        private const string MAKE = "Canon";
        private const string MODEL = "IXUS 40";
        #endregion

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
            this.Closed += new EventHandler(Page_Closed);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                // Get any previous values stored in the registry.
                RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY);
                if (key != null)
                {
                    object val = null;

                    // Get folder history.
                    for (int i = 1; i < 10; i++)
                    {
                        val = key.GetValue(string.Format(REG_SRC_HIST, i));
                        if (val != null)
                        {
                            cbFrom.Items.Add(val);
                        }

                        val = key.GetValue(string.Format(REG_IMG_HIST, i));
                        if (val != null)
                        {
                            cbToImage.Items.Add(val);
                        }

                        val = key.GetValue(string.Format(REG_VID_HIST, i));
                        if (val != null)
                        {
                            cbToVideo.Items.Add(val);
                        }
                    }

                    // Source folder.
                    val = key.GetValue(REG_SRC);
                    if (val != null)
                    {
                        if (cbFrom.Items.Count == 0) cbFrom.Items.Add(val);
                        cbFrom.SelectedValue = val;
                    }

                    // Destination folders.
                    val = key.GetValue(REG_IMG);
                    if (val != null)
                    {
                        if (cbToImage.Items.Count == 0) cbToImage.Items.Add(val);
                        cbToImage.SelectedValue = val;
                    }
                    val = key.GetValue(REG_VID);
                    if (val != null)
                    {
                        if (cbToVideo.Items.Count == 0) cbToVideo.Items.Add(val);
                        cbToVideo.SelectedValue = val;
                    }

                    // Boolean values.
                    val = key.GetValue(REG_OVERWRITE);
                    if (val != null)
                    {
                        bOverwrite = Convert.ToBoolean(val);
                    }

                    val = key.GetValue(REG_SHOW_VID);
                    if (val != null)
                    {
                        bShowVideo = Convert.ToBoolean(val);
                    }

                    // Folder formats.
                    val = key.GetValue(REG_IMAGE_FMT);
                    if (val != null)
                    {
                        imageFormat = val.ToString();
                    }

                    val = key.GetValue(REG_VIDEO_FMT);
                    if (val != null)
                    {
                        videoFormat = val.ToString();
                    }

                    SetImageFolderLabel();
                    SetVideoFolderLabel();
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
            }

            // Don't what these triggered until the values have been set.
            cbToImage.SelectionChanged += new SelectionChangedEventHandler(cbToImage_SelectionChanged);
            cbToVideo.SelectionChanged += new SelectionChangedEventHandler(cbToVideo_SelectionChanged);
        }

        void Page_Closed(object sender, EventArgs e)
        {
            try
            {
                // Store the selected values in the registry for next time.
                RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY, true);
                if (key == null)
                {
                    key = Registry.LocalMachine.CreateSubKey(REG_KEY);
                }
                if (cbFrom.SelectedValue != null)    key.SetValue(REG_SRC, cbFrom.SelectedValue);
                if (cbToImage.SelectedValue != null) key.SetValue(REG_IMG, cbToImage.SelectedValue);
                if (cbToVideo.SelectedValue != null) key.SetValue(REG_VID, cbToVideo.SelectedValue);
                if (imageFormat.Length > 0)          key.SetValue(REG_IMAGE_FMT, imageFormat);
                if (videoFormat.Length > 0)          key.SetValue(REG_VIDEO_FMT, videoFormat);
                key.SetValue(REG_OVERWRITE, bOverwrite, RegistryValueKind.DWord);
                key.SetValue(REG_SHOW_VID,  bShowVideo, RegistryValueKind.DWord);
            }
            catch (Exception ex)
            {
                LogError(ex);
            }
        }

        private void Close_Clicked(object sender, RoutedEventArgs e)
        {
            // Close the application.
            Close();
        }

        private void MenuItem_About(object sender, RoutedEventArgs e)
        {
            System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly();
            ShowMessage(PROGRAM, string.Format(FORMAT_3, ABOUT, a.GetName().Version.ToString()));
        }

        private void MenuItem_Settings(object sender, RoutedEventArgs e)
        {
            // Show Settings dialog.
            Settings dlg = new Settings();
            dlg.Owner = this;

            // Set values in dialog.
            dlg.OverwriteFiles = bOverwrite;
            dlg.ShowVideoImages = bShowVideo;
            dlg.ImageFormat = imageFormat;
            dlg.VideoFormat = videoFormat;

            // Update values if OK pressed.
            if (Convert.ToBoolean(dlg.ShowDialog()))
            {
                bOverwrite = dlg.OverwriteFiles;
                bShowVideo = dlg.ShowVideoImages;
                imageFormat = dlg.ImageFormat;
                videoFormat = dlg.VideoFormat;

                SetImageFolderLabel();
                SetVideoFolderLabel();
            }
        }

        private void MenuItem_ClearHistory(object sender, RoutedEventArgs e)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY, true);
            if (key != null)
            {
                for (int i = 1; i < 10; i++)
                {
                    try
                    {
                        key.DeleteValue(string.Format(REG_SRC_HIST, i));
                    }
                    catch (Exception) { }

                    try
                    {
                        key.DeleteValue(string.Format(REG_IMG_HIST, i));
                    }
                    catch (Exception) { }

                    try
                    {
                        key.DeleteValue(string.Format(REG_VID_HIST, i));
                    }
                    catch (Exception) { }
                }
            }
        }

        private void cbToImage_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SetImageFolderLabel();
        }

        private void cbToVideo_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SetVideoFolderLabel();
        }

        private void SelectFromFolder(object sender, RoutedEventArgs e)
        {
            // Capture the Source directory.
            GetFolderSelection(FileType.None);
        }

        private void SelectImageFolder(object sender, RoutedEventArgs e)
        {
            // Capture the Destination directory.
            if (GetFolderSelection(FileType.Image))
            {
                SetImageFolderLabel();
            }
        }

        private void SelectVideoFolder(object sender, RoutedEventArgs e)
        {
            // Capture the Destination directory.
            if (GetFolderSelection(FileType.Video))
            {
                SetVideoFolderLabel();
            }
        }

        private bool GetFolderSelection(FileType type)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();

            string path = string.Empty;
            string regKey = string.Empty;
            System.Windows.Controls.ComboBox combo = null;
            switch (type)
            {
                case FileType.Image:
                {
                    dlg.ShowNewFolderButton = true;
                    dlg.Description = DESC_IMG;
                    path = getImageFolder();
                    regKey = REG_IMG_HIST;
                    combo = cbToImage;
                    break;
                }
                case FileType.Video:
                {
                    dlg.ShowNewFolderButton = true;
                    dlg.Description = DESC_VID;
                    path = getVideoFolder();
                    regKey = REG_VID_HIST;
                    combo = cbToVideo;
                    break;
                }
                default:
                {
                    dlg.ShowNewFolderButton = false;
                    dlg.Description = DESC_SRC;
                    path = getSourceFolder();
                    regKey = REG_SRC_HIST;
                    combo = cbFrom;
                    break;
                }
            }

            // Before setting the selected path, make sure that is exists.
            if (Directory.Exists(path)) dlg.SelectedPath = path;

            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if (!dlg.SelectedPath.Equals(combo.SelectedValue))
                {
                    if (SaveFolderHistory(dlg.SelectedPath, regKey))
                    {
                        combo.Items.Add(dlg.SelectedPath);
                    }
                    combo.SelectedValue = dlg.SelectedPath;
                    return true;
                }
            }

            return false;
        }

        private string getSourceFolder()
        {
            object val = cbFrom.SelectedValue;
            if (val != null)
            {
                return val.ToString();
            }
            else
            {
                return string.Empty;
            }
        }

        private string getImageFolder()
        {
            object val = cbToImage.SelectedValue;
            if (val != null)
            {
                return val.ToString();
            }
            else
            {
                // It would be nice to default this to "My Documents\My Pictures".
                return string.Empty;
            }
        }

        private string getVideoFolder()
        {
            object val = cbToVideo.SelectedValue;
            if (val != null)
            {
                return val.ToString();
            }
            else
            {
                // It would be nice to default this to "My Documents\My Videos".
                return string.Empty;
            }
        }

        private bool SaveFolderHistory(string folder, string reg_name)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY, true);
            if (key == null)
            {
                // Nothing in the registry yet so just create it.
                key = Registry.LocalMachine.CreateSubKey(REG_KEY);
                key.SetValue(string.Format(reg_name, 1), folder);
            }
            else
            {
                // Iterate through the history and store the folder if valid.
                for (int i = 1; i < 10; i++)
                {
                    object val = key.GetValue(string.Format(reg_name, i));
                    if (val == null)
                    {
                        // We have a gap so store it.
                        key.SetValue(string.Format(reg_name, i), folder);
                        break;
                    }
                    else if (folder.Equals(val))
                    {
                        // Folder already exists.
                        return false;
                    }
                }
            }
            return true;
        }

        private void Copy_Clicked(object sender, RoutedEventArgs e)
        {
            try
            {
                string src = getSourceFolder();
                if (src.Length > 0 && getImageFolder().Length > 0 && getVideoFolder().Length > 0)
                {
                    // Only process the files in this folder.
                    DirectoryInfo root = new DirectoryInfo(src);
                    FileInfo[] images = root.GetFiles();

                    // Show progress bar.
                    ProgressWindow dlg = new ProgressWindow(images.Length);
                    dlg.Owner = this;
                    dlg.Show();

                    // Iterate though each file and copy it to the new folder.
                    foreach (FileInfo file in images)
                    {
                        // Update progress bar.
                        dlg.UpdateStatus(file.FullName, bShowVideo);
                        CopyFile(file);
                    }

                    dlg.Finish();
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
            }
        }

        private void CopyFile(FileInfo file)
        {
            try
            {
                string dirName = string.Empty;
                switch (file.Extension.ToUpper())
                {
                    case JPG:
                    {
                        // Get the Exif properties used to generate the copy to folder.
                        ExifPropertyReader exif = new ExifPropertyReader(file);
                        dirName = GetFilePath(FileType.Image, exif.Date, exif.Make, exif.Model);
                        break;
                    }
                    case AVI:
                    {
                        dirName = GetFilePath(FileType.Video, file.CreationTime, string.Empty, string.Empty);
                        break;
                    }
                    default:
                    {
                        return;
                    }
                }

                // Check directory exists, if not create it.
                if (!Directory.Exists(dirName))
                {
                    Directory.CreateDirectory(dirName);
                }

                // Copy the file to new folder.
                string fileName = string.Format(FORMAT_1, dirName, file.Name);
                try
                {
                    File.Copy(file.FullName, fileName, bOverwrite);
                }
                catch (IOException ex)
                {
                    // Handle existing files.
                    Message dlg = new Message();
                    dlg.Owner = this;
                    dlg.lblTitle.Content = FILE_EXISTS;
                    dlg.txtMessage.Text = string.Format(FORMAT_4, ex.Message, OVERWRITE);

                    // Show dialog.
                    if (Convert.ToBoolean(dlg.ShowDialog()))
                    {
                        // Force overwrite.
                        File.Copy(file.FullName, fileName, true);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
            }
        }

        private void SetImageFolderLabel()
        {
            // Display an example of this folder format.
            string path = GetFilePath(FileType.Image, DateTime.Today, MAKE, MODEL);
            txtImageFolder.Text = GetReadablePath(path, MAX_LENGTH);
        }

        private void SetVideoFolderLabel()
        {
            // Display an example of this folder format.
            string path = GetFilePath(FileType.Video, DateTime.Today, string.Empty, string.Empty);
            txtVideoFolder.Text = GetReadablePath(path, MAX_LENGTH);
        }

        private string GetReadablePath(string path, int length)
        {
            if (path.Length > length)
            {
                // Starts with "\\NAS-212\" or "C:\"
                var start_pos = path.StartsWith(@"\\") ? 2 : 0;
                var start_length = path.IndexOf(@"\", start_pos) + 1;

                // Split folders into an array.
                string[] arr = path.Split('\\');

                // Start by adding the last folder to the path.
                string shortPath = arr[arr.Length - 1];

                // Point at the second to last folder in the list, we just used the last one.
                int i = arr.Length - 2;

                // Loop backwards through the folders until the max length is reached.
                // Add start_length + 4 to the length to allow for "\\NAS-212\...\" or "C:\...\"
                while (i > 0 && shortPath.Length + arr[i].Length + start_length + 4 < length)
                {
                    shortPath = string.Format(FORMAT_1, arr[i], shortPath);
                    i--;
                }

                // End up with a path like this "C:\...\My Pictures\Canon\IXUS 40\2009\01_01_2009".
                path = string.Format(FORMAT_2, path.Substring(0, start_length), shortPath);
            }
            return path;
        }

        private string GetFilePath(FileType type, DateTime date, string make, string model)
        {
            string format = string.Empty;
            string path = string.Empty;
            switch (type)
            {
                case FileType.Image:
                {
                    format = imageFormat;
                    path = getImageFolder();
                    break;
                }
                case FileType.Video:
                {
                    format = videoFormat;
                    path = getVideoFolder();
                    break;
                }
            }

            if (path.Length > 0)
            {
                // Canon specific processing.
                if (model.Contains(CANON_DIGITAL))
                {
                    // Remove make from model description.
                    model = model.Replace(CANON_DIGITAL, string.Empty);
                }

                // Generate the selected folder format.
                StringBuilder sFormat = new StringBuilder();
                switch (format)
                {
                    case "_8":
                        // Make\Model\Year\Date
                        sFormat.AppendFormat("{0}\\{1}\\{2:yyyy}\\{2:yyyy_MM_dd}", make, model, date);
                        break;
                    case "_7":
                        // Make\Model\Year
                        sFormat.AppendFormat("{0}\\{1}\\{2:yyyy}", make, model, date);
                        break;
                    case "_6":
                        // Model\Year\Date
                        sFormat.AppendFormat("{0}\\{1:yyyy}\\{1:yyyy_MM_dd}", model, date);
                        break;
                    case "_5":
                        // Model\Year
                        sFormat.AppendFormat("{0}\\{1:yyyy}", model, date);
                        break;
                    case "_4":
                        // Year\Month
                        sFormat.AppendFormat("{0:yyyy}\\{0:MMM}", date);
                        break;
                    case "_3":
                        // Year\Date
                        sFormat.AppendFormat("{0:yyyy}\\{0:yyyy_MM_dd}", date);
                        break;
                    case "_2":
                        // Year
                        sFormat.AppendFormat("{0:yyyy}", date);
                        break;
                    case "_1":
                        // Date
                        sFormat.AppendFormat("{0:yyyy_MM_dd}", date);
                        break;
                }
                return string.Format(FORMAT_1, path, sFormat.ToString());
            }
            return string.Empty;
        }

        private void ShowMessage(string title, string msg)
        {
            // Show message.
            Message dlg = new Message();
            dlg.Owner = this;
            dlg.lblTitle.Content = title;
            dlg.txtMessage.Text = msg;
            dlg.btnNo.Visibility = Visibility.Hidden;
            dlg.btnYes.Content = OK;
            dlg.ShowDialog();
        }

        private void LogError(Exception ex)
        {
            // Create source if required.
            if (!EventLog.SourceExists(PROGRAM))
            {
                EventLog.CreateEventSource(PROGRAM, "Application");
            }

            // Log error.
            EventLog.WriteEntry(PROGRAM, ex.ToString(), EventLogEntryType.Error);
        }
    }
}